`timescale 1ns / 1ns

//双发射分支预测
//注意
//（1）只有branch_taken==1时，branch_traget的值才会被用到，才能更新BTB表
//     不清楚外部的ID阶段是否能直接给出branch_taken（是否进行跳转）。
//     如果不能，需要加对应代码进行判断
//（2）这个代码是基于IF和ID差了一个时钟，只记录了上次地址last_PC，和ID结果进行对应
//     想着是：将地址输入IF时，同时输入到branch中，branch预测下一时钟的跳转地址给IF
//              并且在下一时钟的ID也能给出branch_taken的结果
//    如果将其加入九级流水中，IF，branch，ID
//    IF和ID会差了两个时钟，需要重新改代码，
//    需要记录上上次地址last_last_PC。然后和ID的branch_taken和branch_traget对应起来
// （3）考虑指令存储器的大小，其只有12位为有效位，因此BTB表设置成12*2=24位宽
//      BTB表只有24位，高12位存PC地址的[13:2]，低12位存跳转地址的[13:2]。[1:0]不存，恒为0
//      输出的话根据当前输入PC的[31:14]位对跳转地址进行补全。
//      这样可能会出错，但能减小BTB所占用的寄存器


module branchPredictionUnit (
  input clk,
  input rst,
  input [31:0] pc_address,  // 输入PC地址 指令1       IF阶段给
  input [31:0] pc_address2, //输入PC地址 指令2        IF阶段给
  
  input branch_taken,       // 输入指令1是否跳转     ID阶段给
  input [31:0] branch_traget,      //指令1跳转地址   ID阶段给  当branch_taken为1时，这个值才被用到
  input branch_taken2,       // 输入指令2是否跳转    ID阶段给
  input [31:0] branch_traget2,      //指令2跳转地址  ID阶段给
 
  output wire branch_prediction,   // 输出指令1预测结果
  output wire [31:0] branch_prediction_traget, //输出指令1跳转地址
  output wire branch_prediction2,   // 输出指令2预测结果
  output wire [31:0] branch_prediction_traget2 //输出指令2跳转地址
);

  parameter TABLE_SIZE = 256;    // PHT项数大小
  parameter COUNTER_WIDTH = 2;    // PHT计数器宽度
  reg [COUNTER_WIDTH-1:0] PHT [0:TABLE_SIZE-1]; //PHT表
  reg [23:0] BTB [0:TABLE_SIZE-1];              //BTB表
  integer      i;

// 指令1
  reg [31:0] last_PC=32'b0;     //记录上一个PC地址
  reg       taken;              //记录跳不跳
  
// 指令2
  reg [31:0] last_PC2=32'b0;     //记录上一个PC地址
  reg       taken2;              //记录跳不跳
  
  always @(posedge clk or negedge rst) begin
    if(!rst)begin      //PHT和BTB表初始化
        for (i = 0; i < TABLE_SIZE; i = i + 1) begin
            PHT[i] <= 2'b01;        //初始值置01
            BTB[i] <= 2'b00;
        end
    end
    else begin         
        last_PC[31:0]<=pc_address[31:0];
        last_PC2[31:0]<=pc_address2[31:0];
        taken=branch_taken;
        taken2=branch_taken2;
        case(taken)                     //对指令1更新PHT表
            1'b0 : begin            
                        if(PHT[last_PC[9:2]]==2'b00) begin
                             PHT[last_PC[9:2]]=0;
                        end
                        else begin
                            PHT[last_PC[9:2]] = PHT[last_PC[9:2]]-2'b01;
                        end
                    end
            1'b1 : begin        //只有是跳转指令的情况下，才更新BTB表
                        if(PHT[last_PC[9:2]]==2'b11) begin
                            PHT[last_PC[9:2]]=2'b11;
                            BTB[last_PC[9:2]]={last_PC[13:2],branch_traget[13:2]}; 
                        end
                        else begin
                            PHT[last_PC[9:2]] = PHT[last_PC[9:2]]+2'b01;
                            BTB[last_PC[9:2]]={last_PC[13:2],branch_traget[13:2]}; 
                        end
                    end
        endcase
        case(taken2)            //对指令2更新PHT表
            1'b0 : begin            
                        if(PHT[last_PC2[9:2]]==2'b00) begin
                             PHT[last_PC2[9:2]]=0;
                        end
                        else begin
                            PHT[last_PC2[9:2]] = PHT[last_PC2[9:2]]-2'b01;
                        end
                    end
            1'b1 : begin        //只有是跳转指令的情况下，才更新BTB表
                        if(PHT[last_PC2[9:2]]==2'b11) begin
                            PHT[last_PC2[9:2]]=2'b11;
                            BTB[last_PC2[9:2]]={last_PC2[13:2],branch_traget2[13:2]}; 
                        end
                        else begin
                            PHT[last_PC2[9:2]] = PHT[last_PC2[9:2]]+2'b01;
                            BTB[last_PC2[9:2]]={last_PC2[13:2],branch_traget2[13:2]}; 
                        end
                    end
        endcase
    end
  end

//指令1查找BTB表 
assign branch_prediction=PHT[pc_address[9:2]][1];
//如果PC地址和BTB表地址一致，则输出跳转地址，否则输出全0；
assign branch_prediction_traget= (pc_address[13:2]==BTB[pc_address[9:2]][23:12]) ?{pc_address[31:14], BTB[pc_address[9:2]][11:0],2'b00}: 32'b0;

//指令2查找BTB表 
assign branch_prediction2=PHT[pc_address2[9:2]][1];
assign branch_prediction_traget2= (pc_address2[13:2]==BTB[pc_address2[9:2]][23:12]) ?{pc_address2[31:14],BTB[pc_address2[9:2]][11:0],2'b00}: 32'b0;

endmodule